#   Copyright 2008 Wilker Lucio
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import pygtk
pygtk.require('2.0')

import gtk
import threading
import math

from urllib2 import urlopen
from musicfinder.provider import *

import gobject

gtk.gdk.threads_init()
gtk.gdk.threads_enter()

class MainWindow(gtk.Window):
    # close the window and quit
    def delete_event(self, widget, event, data=None):
        gtk.main_quit()
        return False
    
    def __init__(self):
        #call parent
        super(MainWindow, self).__init__()
        
        #initialize window
        self.set_title('Music Finder')
        self.connect("delete_event", self.delete_event)
        self.set_size_request(640, 480)
        
        #main notebook
        self.notebook = gtk.Notebook()
        self.notebook.set_tab_pos(gtk.POS_TOP)
        
        #create search area
        self.searchArea = SearchArea(self)
        
        #create downloads area
        self.downloadArea = DownloadArea()
        
        #add search tab
        searchLabel = gtk.Label('Search')
        searchLabel.show()
        
        self.notebook.append_page(self.searchArea.widget, searchLabel)
        
        #add downloads tab
        downloadLabel = gtk.Label('Downloads')
        downloadLabel.show()
        
        self.notebook.append_page(self.downloadArea.widget, downloadLabel)
        
        #add about
        aboutLabel = gtk.Label('About')
        about = gtk.Label('Create by: Wilker\nVersion: 0.0.2')
        self.notebook.append_page(about, aboutLabel)
        
        #add notebook to window
        self.add(self.notebook)
        
        #show all
        self.show_all()
        
        #set current tab
        self.notebook.set_current_page(0)
    
    def main(self):
        gtk.main()

class MusicInfo():
    def __init__(self, title = "", artist = "", length = "", url = ""):
        self.title = title
        self.artist = artist
        self.length = length
        self.url = url

class SearchArea():
    def __init__(self, main):
        #assign main reference
        self.main = main
        
        #create search input
        self.searchText = gtk.Entry()
        self.searchText.connect('key-press-event', self.handleKey)
        
        #create search button
        self.searchButton = gtk.Button('Search')
        self.searchButton.connect('clicked', self.doSearch)
        
        #organize search input
        searchInputBox = gtk.HBox()
        searchInputBox.pack_start(self.searchText)
        searchInputBox.pack_start(self.searchButton, False)
        
        #create search notebook
        self.searchNotebook = gtk.Notebook()
        self.searchNotebook.set_tab_pos(gtk.POS_TOP)
        
        #organize widget
        self.widget = gtk.VBox()
        self.widget.pack_start(searchInputBox, False)
        self.widget.pack_start(self.searchNotebook)
    
    def handleKey(self, widget, event, data = None):
        if event.keyval == 65293:
            self.doSearch(None)
    
    def doSearch(self, widget, data = None):
        #get search text
        search = self.searchText.get_text()
        
        #cancel if has no search
        if not search:
            return
        
        #create search grid
        searchGrid = SearchAreaGrid(self.main)
        
        #create label for notebook
        label = gtk.Label(self.searchText.get_text())
        label.show()
        
        #create new tab
        self.searchNotebook.set_current_page(self.searchNotebook.append_page(searchGrid.widget, label))
        
        #create providers
        searchGrid.start_search(search)
        
        #clear search input
        self.searchText.set_text('')

class SearchAreaGrid():
    def __init__(self, main):
        #assign main
        self.main = main
        
        #create store
        self.store = gtk.ListStore(str, str, str, str, 'gboolean', gobject.TYPE_PYOBJECT)
        
        #create sort model
        self.sortModel = gtk.TreeModelSort(self.store)
        self.sortModel.set_sort_column_id(1, gtk.SORT_ASCENDING)
        
        #create view
        self.treeview = gtk.TreeView(self.sortModel)
        self.treeview.connect('row-activated', self.row_active)
        
        #create cells
        cells = [gtk.CellRendererText()] * 4
        
        for cell in cells:
            cell.set_property('foreground', 'blue')
        
        #create columns
        columns = [None] * 4
        columns[0] = gtk.TreeViewColumn('Artist')
        columns[1] = gtk.TreeViewColumn('Title')
        columns[2] = gtk.TreeViewColumn('Length')
        columns[3] = gtk.TreeViewColumn('URL')
        
        for i in range(4):
            columns[i].pack_start(cells[i], True)
            columns[i].set_sort_column_id(i)
            columns[i].set_attributes(cells[i], text=i, foreground_set = 4)
            columns[i].set_resizable(True)
            
            self.treeview.append_column(columns[i])
        
        #create scrollable area
        scroll = gtk.ScrolledWindow()
        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scroll.add(self.treeview)
        
        #create progress bar
        self.progress = gtk.ProgressBar()
        self.progress.set_size_request(-1, 20)
        
        #create box
        self.widget = gtk.VBox()
        self.widget.pack_start(scroll)
        self.widget.pack_start(self.progress, False)
        
        #show all
        self.widget.show()
        scroll.show()
        self.treeview.show()
        self.progress.show()
    
    def start_search(self, search):
        #basic vars
        self.total = 0
        self.loaded = 0
        
        #set status
        self.progress.set_text('Loading providers...')
        
        #create providers
        self.providers = [PlaylistProvider(), Mp3TubeProvider()]
        self.waiting = len(self.providers)
        
        #start search threads
        for provider in self.providers:
            searchThread = threading.Thread(target = provider.search, args = (search, self.partial_load))
            searchThread.setDaemon(True)
            searchThread.start()
    
    def row_active(self, treeview, path, view_column, *args):
        sorted_path = self.sortModel.convert_path_to_child_path(path)
        sorted_iter = self.store.get_iter(sorted_path)
        
        if not self.store.get_value(sorted_iter, 4):
            music = self.store.get(sorted_iter, 5)[0]
            
            self.main.downloadArea.download(music)
            self.store.set_value(sorted_iter, 4, True)
    
    def partial_load(self, provider, status, data = None):
        #debug report
        #print provider, 'sent report', status, 'with', data
        
        #enter in gtk thread
        gtk.gdk.threads_enter()
        
        if status == PROVIDER_REPORT_PARTIAL:
            self.loaded += 1
            
            #append musics to grid
            for music in data:
                self.store.append([music.artist, music.title, music.length, music.url, False, music])
        elif status == PROVIDER_REPORT_LENGTH:
            self.total += data
            self.waiting -= 1
        elif status == PROVIDER_REPORT_NORESULTS:
            self.waiting -= 1
            
            if self.waiting == 0 and self.loaded == 0:
                self.progress.set_text('No results for your search')
        elif status == PROVIDER_REPORT_ERROR:
            self.waiting -= 1
            
            if self.waiting == 0 and self.loaded == 0:
                self.progress.set_text('No results for your search')
        
        #update progress bar
        self.update_progress()
        
        #leave gtk thread
        gtk.gdk.threads_leave()
    
    def update_progress(self):
        if self.waiting == 0 and self.total > 0:
            fraction = self.loaded / float(self.total) 
            
            self.progress.set_text(str(round(fraction * 100, 2)) + '%')
            self.progress.set_fraction(fraction)

class DownloadArea():
    def __init__(self):
        #create store
        self.store = gtk.ListStore(str, str, int, str)
        
        #create sort model
        self.sortModel = gtk.TreeModelSort(self.store)
        self.sortModel.set_sort_column_id(1, gtk.SORT_ASCENDING)
        
        #create view
        self.treeview = gtk.TreeView(self.sortModel)
        
        #create cells
        cells = [None] * 4
        cells[0] = gtk.CellRendererText()
        cells[1] = gtk.CellRendererText()
        cells[2] = gtk.CellRendererProgress()
        cells[3] = gtk.CellRendererText()
        
        #create columns
        columns = [None] * 4
        columns[0] = gtk.TreeViewColumn('Artist')
        columns[1] = gtk.TreeViewColumn('Title')
        columns[2] = gtk.TreeViewColumn('Percentage')
        columns[3] = gtk.TreeViewColumn('Status')
        
        for i in range(4):
            columns[i].pack_start(cells[i], True)
            columns[i].set_sort_column_id(i)
            
            if i != 2:
                columns[i].set_attributes(cells[i], text=i)
            else:
                columns[i].set_attributes(cells[i], value=i)
            
            columns[i].set_resizable(True)
            
            self.treeview.append_column(columns[i])
        
        #create scrollable area
        scroll = gtk.ScrolledWindow()
        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
        scroll.add(self.treeview)
        
        #set widget
        self.widget = scroll
        
        #show all
        self.widget.show()
        self.treeview.show()
    
    def download(self, music):
        iter = self.store.append([music.artist, music.title, 0, ''])
        
        thread = threading.Thread(target = self.do_download, args = (music, iter))
        thread.setDaemon(True)
        thread.start()
    
    def do_download(self, music, iter):
        self.set_status('Getting real path...', iter)
        
        url = music.get_real_path()
        
        self.set_status('Initializing download...', iter)
        
        try:
            response = urlopen(url)
        except Exception, e:
            self.set_status('Error loading url: ' + str(e), iter)
            return
        
        self.set_status('Getting file size...', iter)
        
        headers = response.info()
        
        output = open('%s - %s.mp3' % (music.artist, music.title), 'wb')
        
        readed = 0
        total = int(headers['Content-Length'])
        chunck = 10240
        
        self.set_status('Downloading...', iter)
        
        while True:
            buffer = response.read(chunck)
            
            if not buffer:
                break
            
            output.write(buffer)
            readed += len(buffer)
            
            percent = math.floor(readed / float(total) * 100)
            
            gtk.gdk.threads_enter()
            self.store.set_value(iter, 2, percent)
            gtk.gdk.threads_leave()
        
        output.close()
        
        self.set_status('Done', iter)
    
    def set_status(self, message, iter):
        gtk.gdk.threads_enter()
        self.store.set_value(iter, 3, message)
        gtk.gdk.threads_leave()